package main;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.List;
import java.util.Map;

import utils.BlockUtils;
import utils.FileInfo;
import utils.FileType;

import com.google.common.collect.Lists;

public class Main {
	private static Map<Integer, Boolean> bitMap;
	/**
	 * @param args
	 */
	public static void main(String[] args) {
		if(args.length != 3) {
			System.out.println("This command need 3 args");
			System.exit(1);
		}
		String root = args[0];
		String src = args[1];
		String dest = args[2];
		root = BlockUtils.prepareRoot(root);
		bitMap = BlockUtils.createBitMap(new File(root));
		src = BlockUtils.prepareDest(src, root);
		dest = BlockUtils.prepareDest(dest, root);
		File file1 = BlockUtils.checkDestFile(root, dest);
		if(file1 == null) {
			System.out.println("File doesn't exists in dir " + dest);
			System.exit(1);
		}
		File file2 = BlockUtils.checkDestFile(root, src);
		if(file2 == null) {
			System.out.println("File doesn't exists in dir " + src);
			System.exit(1);
		}
		copy(file2, file1, root);
	}

	
	private static void copy(File src, File dest, String root) {
		FileInfo fileInfo = BlockUtils.getFileInfo(src);
		if(fileInfo.getType().equals(FileType.CATALOGUE_TYPE)) {
			copyCatalogue(src, dest, root);
		} else {
			copyFile(src, dest, root);
		}
	}


	private static void copyFile(File src, File dest, String root) {
		FileInfo info = BlockUtils.getFileInfo(src);
		List<File> files = Lists.newArrayList();
		Integer number = 0;
		try {
			if (src.canRead() && dest.canWrite()) {
				//Список файлов на очистку если что-то пойдёт не так
				String name = info.getName();
				FileInputStream fin = null;
				FileOutputStream fout = null;
				try {
					int length = info.getSize();
					byte[] b = new byte[length];
					b = BlockUtils.getFileData(src, root);
					int currentLength = 0;
					int offsetForSrc = 0;
					number = BlockUtils.getNumberEmptyBlock(bitMap);
					Integer numb;
					if(number == null) {
						System.out.println("There is no empty block in File System");
						System.exit(1);
					}
					BlockUtils.addCataloguePointer(dest, number, root);
					//Этот блок будет содержать мета-данные
					File f2 = BlockUtils.getBlockByNumber(number, root);
					f2 = BlockUtils.writeToFile(FileType.FILE_TYPE, name, f2,(int) info.getSize());
					files.add(f2);
					boolean firstblock = true;
					fin = new FileInputStream(f2);
					long length2 = f2.length();
					byte[] b2 = new byte[(int) length2];
					fin.read(b2);
					fin.close();
					fout = new FileOutputStream(f2);
					while(currentLength != (length - 1 - offsetForSrc)) {
						if(firstblock) {
							int i = currentLength + BlockUtils.METADATOFFSET;
							//4 тут размер последней ячейки зарезервированной под ссылку на следующий блок
							int j = i + 4;
							if(j == length2) {
								numb = BlockUtils.getNumberEmptyBlock(bitMap);
								if(numb == null) {
									for(File file : files) {
										BlockUtils.clearBlock(file, b2.length);
									}
									System.out.println("There is no empty block in File System");
									System.exit(1);
								}
								BlockUtils.setNextBlock(b2, numb);
								fout.write(b2);
								f2 = BlockUtils.getBlockByNumber(numb, root);
								fin = new FileInputStream(f2);
								b2 = new byte[(int) length2];
								fin.read(b2);
								fin.close();
								fout = new FileOutputStream(f2);
								offsetForSrc = currentLength;
								currentLength = 0;
								firstblock = false;
								continue;
							}
							b2[i] = b[offsetForSrc + currentLength];
						} else {
							int i = currentLength + 4;
							//4 тут размер последней ячейки зарезервированной под ссылку на следующий блок
							if(i == length2) {
								numb = BlockUtils.getNumberEmptyBlock(bitMap);
								if(numb == null) {
									for(File file : files) {
										BlockUtils.clearBlock(file, b2.length);
									}
									System.out.println("There is no empty block in File System");
									System.exit(1);
								}
								BlockUtils.setNextBlock(b2, numb);
								fout.write(b2);
								f2 = BlockUtils.getBlockByNumber(numb, root);
								length2 = f2.length();
								fin = new FileInputStream(f2);
								b2 = new byte[(int) length2];
								fin.read(b2);
								fout = new FileOutputStream(f2);
							}
							b2[currentLength] = b[currentLength + offsetForSrc];
						}
						currentLength++;
					}
					fout.write(b2);
				} catch (FileNotFoundException e) {
					e.printStackTrace();
				} catch (IOException e) {
					e.printStackTrace();
				} finally {
					try {
						if(fin != null)
							fin.close();
						if(fout != null)
							fout.close();
					} catch (IOException e) {
						e.printStackTrace();
					}
		        }
			}
		} catch(Exception e) {
			for(File file : files) {
				BlockUtils.clearBlock(file, file.length());
			}
			BlockUtils.removeCataloguePointer(src, number, root);
			e.printStackTrace();
		}
	}


	private static void copyCatalogue(File src, File dest, String root) {
		FileInfo info = BlockUtils.getFileInfo(src);
		List<File> files = Lists.newArrayList();
		Integer number = 0;
		if (src.canRead() && dest.canWrite()) {
			number = BlockUtils.getNumberEmptyBlock(bitMap);
			File f2 = BlockUtils.getBlockByNumber(number, root);
			f2 = BlockUtils.writeToFile(FileType.CATALOGUE_TYPE, info.getName(), f2, info.getSize());
			files.add(f2);
			BlockUtils.addCataloguePointer(dest, number, root);
			List<File> listFiles = BlockUtils.getListFiles(src, root);
			for(File file :listFiles) {
				copy(file, f2, root);
			}
		}	
	}
}
